import os,pickle
import time
from twisted.internet import reactor

class ProvTrackingManager(object):
    triggers = ["!moadd", "!army", "your","!moall","a","we","alas,","our","!status","!armies"]
    channels = {}
    name = "tracking"
    help = ["Province tracking commands:",
            "%c6!moadd%c <last-sent>"%(03,03),
            "%c6!army%c <days-left>"%(03,03),
            "%c6!armies%c"%(03,03),
            "%c6!status%c <irc-nick>"%(03,03)]
    
    def __init__(self, messenger):
        self.messenger = messenger
    
    def command(self, firstword, user, channel, msg):
        if not self.channels.has_key(channel):
            self.channels[channel] = ChannelTracker(channel, self.messenger)
        
        tracker = self.channels[channel]
        words = msg.split()
        if msg.startswith("Your forces arrive at "):
            target = words[4]
            i = 5
            while ':' not in words[i]:
                target = target + " " + words[i]
                i = i+1
            target = target + " " + words[i][:-1]
            tracker.newhit(user, target)
        if msg.startswith("Your army has taken "):
            amount = int(words[4])
            tracker.trad(user, amount)
            hometime = float(msg.split("will take ")[1].split()[0])
            tracker.hometime(user,hometime)
        if msg.startswith("Your army looted "):
            amount = int("".join(words[3][:-3].split(',')))
            tracker.plunder(user, amount)
        if msg.startswith("Your army stole "):
            amount = int("".join(words[3].split(',')))
            tracker.learn(user, amount)
        if msg.startswith("Your army massacred "):
            amount = int("".join(words[3].split(',')))
            tracker.massacre(user, amount)
        if msg.startswith("Your army burned and destroyed "):
            amount = int("".join(words[5].split(',')))
            tracker.raze(user, amount)
        if msg.startswith("Your army has recaptured "):
            amount = int("".join(words[4].split(',')))
            tracker.ambush(user, amount)
        if msg.startswith("Alas, ") or msg.startswith("Our army appears to have failed") or msg.startswith("Your army was no match for") or msg.startswith("Your troops march"):
            tracker.bounce(user)
        if msg.startswith("A tough battle took place"):
            tracker.setmargin(user,"tough battle")
        if msg.startswith("We killed about "):
            if tracker.findpartial(user): return {'usernotice':["Attack detected, please add offense using !moadd."]}
        if firstword == "!moadd":
            if len(words) != 2: return{'usernotice':["Usage: %c6!moadd%c <last-sent>"%(03,03)]}
            result = tracker.moadd(user,words[1])
            if result: return {'usernotice':["Your hit was added to the list. Depending on the hit type, you may want to add army return time using !army."],'chanmsg':[result]}
        if firstword == "!army":
            if len(words) != 2: return{'usernotice':["Usage: %c6!army%c <days-left>"%(03,03)]}
            try:
                days = float(words[1])
                result = tracker.hometime(user,days)
                return {'usernotice':["Army return timer set."]}
            except: 
                return{'usernotice':["Usage: %c6!army%c <days-left>"%(03,03)]}
        if firstword == "!status":
            if len(words) > 2: return{'usernotice':["Usage: %c6!status%c <irc-nick>"%(03,03)]}
            name = user
            if len(words) == 2: name = words[1]            
            return {'chanmsg':[tracker.status(name)]}
        if msg == "!moall":
            return {'chanmsg':tracker.printhits()}
        if msg == "!armies":
            return {'chanmsg':tracker.returntimes()}
        return {}    


class ChannelTracker(object):
    
    def __init__(self, channel, messenger):
        self.partials = []
        self.hits = []
        self.hometimes = {}
        self.channel = channel
        self.messenger = messenger
        self.filename = ".\\" + channel + "_tracker.txt"
        if os.path.exists(self.filename):
            f = open(self.filename, 'r')
            self.hits = pickle.load(f)
            self.hometimes = pickle.load(f)
            self.settimers()
            f.close()
        oldhits = []
        curtime = int(time.time())
        self.hits = filter(lambda hit: hit.deltime > curtime, self.hits)
        self.save()
    
    def returntimes(self):
        output = []
        times = {}
        curtime = int(time.time())
        for p,t in self.hometimes.iteritems():
            if t > curtime:
                times[t] = p
        if len(times):
            ret = times.keys()
            ret.sort()
            for t in ret:
                timeleft = t-curtime
                hours = timeleft/3600
                timeleft = timeleft%3600
                mins = timeleft/60
                output.append("In %02d:%02d, %s's army returns."%(hours,mins,times[t]))
        if len(output) == 0: output.append("No armies out AFAIK.")
        return output                
    
    def status(self, player):
        if self.hometimes.has_key(player):
            htime = self.hometimes[player]
            curtime = int(time.time())
            if htime > curtime:
                timeleft = htime-curtime
                hours = timeleft/3600
                timeleft = timeleft - hours*3600
                minutes = timeleft/60
                return "The army of %s will return in %02d:%02d."%(player,hours,minutes)
            timehome = curtime - htime
            days = timehome/(3600*24)
            timehome = timehome%(3600*24)
            hours = timehome/3600
            timehome = timehome%3600
            minutes = timehome/60
            return "%s's army has been home for %02d:%02d:%02d. (days/hrs/mins)"%(player,days,hours,minutes)
        return "No info on army return time for %s."%(player)
    
    def settimers(self):
        curtime = int(time.time())
        old = []
        for player,htime in self.hometimes.iteritems():
            if htime > curtime:
                reactor.callLater(htime-curtime,self.armyreturned,player)
            else:
                if (curtime - htime) > 3600*24*5: old.append(player)
        for player in old:
            del self.hometimes[player]
    
    def printhits(self):
        if len(self.hits) < 1: return ["No hits recorded."]
        curtime = int(time.time())
        self.hits = filter(lambda hit: hit.deltime > curtime, self.hits)
        self.save()
        return map(lambda hit:hit.string(), self.hits)
    
    def save(self):
        f = open(self.filename, 'w')
        pickle.dump(self.hits, f)
        pickle.dump(self.hometimes, f)
        f.close()
        
    def newhit(self, attacker, target):
        newhit = Hit()
        newhit.attacker = attacker
        newhit.target = target
        newhit.time = int(time.time())
        newhit.deltime = newhit.time + 12*3600
        self.partials = filter(lambda hit: hit.attacker != attacker, self.partials)
        self.partials.append(newhit)        
        reactor.callLater(60,self.cleanup,newhit)
    
    def setmargin(self, attacker, margin):
        for hit in self.partials:
            if hit.attacker == attacker:
                hit.margin = margin
    
    def armyreturned(self, attacker):
        if self.hometimes.has_key(attacker):
            self.messenger(self.channel, "%c10 %s%c's army just came back home."%(03,attacker,03))
    
    def hometime(self, attacker, days):
        hometime = int(time.time()+3600*days)
        self.hometimes[attacker] = hometime
        reactor.callLater(int(3600*days),self.armyreturned,attacker)
        self.save()
        
    def trad(self, attacker, amount):
        for hit in self.partials:
            if hit.attacker == attacker:
                hit.type = "traditional march"
                hit.gain = amount
    
    def bounce(self, attacker):
        hit = self.findpartial(attacker)
        if hit != None:
            hit.type = "bounce"
    
    def learn(self, attacker, amount):
        hit = self.findpartial(attacker)
        if hit != None:
            hit.type = "learn"
            hit.gain = "%dk"%(amount/1000)

    def ambush(self, attacker, amount):
        hit = self.findpartial(attacker)
        if hit != None:
            hit.type = "ambush"
            hit.gain = "%d"%(amount)

    def massacre(self, attacker, amount):
        hit = self.findpartial(attacker)
        if hit != None:
            hit.type = "massacre"
            hit.gain = "%d"%(amount)
                
    def raze(self, attacker, amount):
        hit = self.findpartial(attacker)
        if hit != None:
            hit.type = "raze"
            hit.gain = "%d"%(amount)
                
    def plunder(self, attacker, amount):
        hit = self.findpartial(attacker)
        if hit != None:
            hit.type = "plunder"
            if amount >= 1000000:
                hit.gain = "%.1fM"%(amount/1000000.0)
            else:
                hit.gain = "%dk"%(amount/1000)
    
    def findpartial(self, attacker):
        for hit in self.partials:
            if hit.attacker == attacker:
                return hit
        return None                
                
    def moadd(self, attacker, off):
        found = None
        for hit in self.partials:
            if hit.attacker == attacker and found == None:
                hit.off = off
                found = hit
        if found is not None:
            self.hits.append(found)
            self.partials.remove(found)
            self.save()
            return found.string()          
        
    def cleanup(self, partial):
        if partial in self.partials:
            self.partials.remove(partial)
        
        

class Hit(object):
    attacker = "??"
    target = "??"
    type = ""
    gain = 0
    time = 0
    deltime = 0
    off = "??"
    margin = "??"
    
    def printmargin(self):
        if self.margin == "tough battle": return ""
        return "(oversent)"
    
    def string(self):
        if self.type == "bounced": return "%c10%s %c4BOUNCED%c on %s."
        if self.type == "traditional march":
            return "%s,%c10 %s%c hit%c13 %s%c for %d acres. %c6MO: %s %c%s"%(self.timeago(), 03,self.attacker,03,03,self.target,03,self.gain,03,self.off,03,self.printmargin())
        if self.type == "plunder":
            return "%s,%c10 %s%c plundered%c13 %s%c for %s gc. %c6MO: %s %c%s"%(self.timeago(), 03,self.attacker,03,03,self.target,03,self.gain,03,self.off,03,self.printmargin())
        if self.type == "learn":
            return "%s,%c10 %s%c learned%c13 %s%c for %s books. %c6MO: %s %c%s"%(self.timeago(), 03,self.attacker,03,03,self.target,03,self.gain,03,self.off,03,self.printmargin())
        if self.type == "massacre":
            return "%s,%c10 %s%c massacred %s at%c13 %s%c. %c6MO: %s %c%s"%(self.timeago(), 03,self.attacker,03,self.gain,03,self.target,03,03,self.off,03,self.printmargin())
        if self.type == "raze":
            return "%s,%c10 %s%c razed %s of%c13 %s%c's acres. %c6MO: %s %c%s"%(self.timeago(), 03,self.attacker,03,self.gain,03,self.target,03,03,self.off,03,self.printmargin())
        if self.type == "ambush":
            return "%s,%c10 %s%c ambushed %s for %s acres. %c6MO: %s %c%s"%(self.timeago(), 03,self.attacker,03,self.target,self.gain,03,self.off,03,self.printmargin())
        if self.type == "bounce":
            return "%s,%c10 %s %c4BOUNCED%c a hit on%c13 %s%c. %c6MO: %s %c(bounce)"%(self.timeago(),03,self.attacker,03,03,03,self.target,03,03,self.off,03)
        return "%s, %s hit %s, but I didnt quite understand the hit type."%(self.timeago(), self.attacker, self.target)
    
    def timeago(self):
        timediff = int(time.time())-self.time
        hours = timediff/3600
        timediff = timediff%3600
        minutes = timediff/60
        return "%02d:%02d ago"%(hours,minutes)


if __name__ == "__main__":
    def msg(channel, msg):
        print channel, ": ", msg
    
    ## Create accurate simulation, by spawning a whole bot for it, and overriding the messenger functions
    from ircDivBot import DivBot
    import tracking
    b = DivBot()
    t = tracking.ProvTrackingManager(msg)
    for p in b.plugins:
        if type(p) == type(t): b.plugins.remove(p)
    b.plugins.append(t)
    b.msg = msg
    b.notice = msg
    
    ## Start testing messages
    b.privmsg("test_user","test_chan","!time")
    print ""
    
    ## Load input from file
    f = open('test.txt','r')
    rawlines = f.readlines()
    f.close()
    
    stripped = []
    for l in rawlines:
        stripped.append(l.rstrip("\n"))

    for l in stripped:
        b.privmsg("test_user","test_chan",l)
        
    print b.nickname
    print b.password